package cn.codeinn.generator.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipOutputStream;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Component;

import cn.codeinn.generator.entity.Table;

import cn.vkits.core.collection.CollUtil;
import cn.vkits.core.date.DateUtil;
import cn.vkits.core.io.IoUtil;
import cn.vkits.core.io.resource.ClassPathResource;
import cn.vkits.core.lang.Dict;
import cn.vkits.core.util.StrUtil;
import cn.vkits.extra.template.engine.velocity.VelocityUtil;

@Component
public class GenCodeUtil {
	private static Properties properties = new Properties();
	private static Map<String, List<String>> BUSINESS_TEMPLATE_FILE = new HashMap<>();
	private static final String TEMPLATE_PATH = "template/";
	static {
		// 业务处理模版
		BUSINESS_TEMPLATE_FILE.put("Controller", CollUtil.newArrayList("Controller.java.vm"));
		BUSINESS_TEMPLATE_FILE.put("Entity", CollUtil.newArrayList("Entity.java.vm", "Entity.xml.vm"));
		BUSINESS_TEMPLATE_FILE.put("Html", CollUtil.newArrayList("Html.vm", "Js.vm"));
		BUSINESS_TEMPLATE_FILE.put("Mapper", CollUtil.newArrayList("Mapper.java.vm"));
		BUSINESS_TEMPLATE_FILE.put("Service", CollUtil.newArrayList("Service.java.vm", "Service.Impl.java.vm"));
		BUSINESS_TEMPLATE_FILE.put("SQL", CollUtil.newArrayList("SQL.vm"));
		BUSINESS_TEMPLATE_FILE.put("Config", CollUtil.newArrayList("SwaggerConfig.vm", "MvcConfig.vm"));

		try {
			ClassPathResource resource = new ClassPathResource("generator.properties");
			properties.load(resource.getStream());
		} catch (IOException e) {
		}

		// 设置velocity资源加载器
		Properties prop = new Properties();
		prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
		Velocity.init(prop);
	}

	public static void configProcess(ZipOutputStream zip, Table table) throws IOException {
		// 表信息
		Dict tableDict = Dict.create();
		// 设置配置信息
		tableDict.set("package", table.getPackageName()).set("moduleName", table.getModuleName()).set("author", "Auto GenCode").set("email", "victor.yang@kutesmart.com").set("datetime", DateUtil.now());
		// 配置文件类名
		tableDict.set("configClassName", StrUtil.upperFirst(table.getModuleName()));
		// 生成文件
		VelocityContext context = new VelocityContext(tableDict);

		List<String> modules = BUSINESS_TEMPLATE_FILE.get("Config");
		for (String module : modules) {
			Template tpl = Velocity.getTemplate(TEMPLATE_PATH + module, "UTF-8");
			StringWriter sw = new StringWriter();
			VelocityUtil.merge(tpl, context, sw);
			File f = new File(getFileName(module, null, table.getPackageName(), table.getModuleName()));
			if (!f.getParentFile().exists()) {
				f.getParentFile().mkdirs();
			}
			if (!f.exists()) {
				f.createNewFile();
			}
			FileOutputStream out = new FileOutputStream(f);
			IoUtil.writeUtf8(out, false, sw);
			IoUtil.close(sw);
		}
	}

	public static void businessProcess(Map<String, String> tempTable, List<Map<String, String>> columns, Table table) throws IOException {
		Dict tableDict = Dict.create();
		tableDict.putAll(tempTable);
		tableDict.set("hasBigDecimal", false);
		tableDict.set("hasDate", false);
		String classname = (StrUtil.toCamelCase(StrUtil.removeAll(tempTable.get("tableName"), table.getTablePrefix())));
		tableDict.set("classname", classname).set("className", StrUtil.upperFirst(classname)).set("pathName", classname.toLowerCase());
		List<Dict> columsList = new ArrayList<>();
		for (Map<String, String> column : columns) {
			Dict columnDict = Dict.create();
			columnDict.putAll(column);
			String attrname = StrUtil.toCamelCase(column.get("columnName"));
			columnDict.set("attrname", attrname).set("attrName", StrUtil.upperFirst(attrname));
			String attrType = properties.getProperty(columnDict.getStr("dataType"), "unknowType");
			if (!tableDict.getBool("hasBigDecimal") && attrType.equals("BigDecimal")) {
				tableDict.set("hasBigDecimal", true);
			}
			if (!tableDict.getBool("hasDate") && attrType.equals("Date")) {
				tableDict.set("hasDate", true);
			}
			columnDict.set("attrType", attrType);
			if ("PRI".equalsIgnoreCase(column.get("columnKey")) && null == tableDict.get("pk")) {
				tableDict.set("pk", columnDict);
			}
			columsList.add(columnDict);
		}
		tableDict.set("columns", columsList);
		if (null == tableDict.get("pk")) {
			tableDict.set("pk", tableDict.get("colums", new ArrayList<>()).get(0));
		}
		tableDict.set("package", table.getPackageName()).set("moduleName", table.getModuleName()).set("author", "Auto GenCode").set("email", "victor.yang@kutesmart.com").set("datetime", DateUtil.now());
		VelocityContext context = new VelocityContext(tableDict);
		List<String> businessNames = Arrays.asList("Entity,Mapper,Service".split(","));
		for (String businessName : businessNames) {
			for (String module : BUSINESS_TEMPLATE_FILE.get(businessName)) {
				Template tpl = Velocity.getTemplate(TEMPLATE_PATH + module, "UTF-8");
				StringWriter sw = new StringWriter();
				VelocityUtil.merge(tpl, context, sw);
				createFile(getFileName(module, tableDict.getStr("className"), table.getPackageName(), table.getModuleName()), sw);
			}
		}
	}

	private static void createFile(String filepath, StringWriter sw) throws IOException {
		File f = new File(filepath);
		if (!f.getParentFile().exists()) {
			f.getParentFile().mkdirs();
		}
		if (!f.exists() && f.createNewFile()) {
			FileOutputStream out = new FileOutputStream(f);
			IoUtil.writeUtf8(out, false, sw);
			IoUtil.close(sw);
		}
	}

	/**
	 * 获取文件名
	 */
	public static String getFileName(String template, String className, String packageName, String moduleName) {
		String packagePath = "src" + File.separator + "main" + File.separator + "java" + File.separator;
		if (StrUtil.isNotBlank(packageName)) {
			packagePath += packageName.replace(".", File.separator) + File.separator + moduleName + File.separator;
		}
		switch (template) {
		case "Entity.java.vm":
			return packagePath + "entity" + File.separator + className + ".java";
		case "Mapper.java.vm":
			return packagePath + "mapper" + File.separator + className + "Mapper.java";
		case "Service.java.vm":
			return packagePath + "service" + File.separator + className + "Service.java";
		case "Service.Impl.java.vm":
			return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
		case "Entity.xml.vm":
			return "src" + File.separator + "main" + File.separator + "resources" + File.separator + "mapper-" + moduleName + File.separator + className + "Mapper.xml";
		}
		return null;
	}
}
